﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Net.Sockets;
using System.Net;

namespace BGF_POS에뮬
{
    public partial class Form1 : Form
    {
        private const String INI_FILE = "\\default.ini";
        // 서버
        private String m_server_ip;
        private String m_server_port;
        // 헤더정보
        private Dictionary<String, int> dicHeader = new Dictionary<string, int>();
        // 제품정보
        private Dictionary<String, int> dicProduct = new Dictionary<string, int>();
        // 선택박스별요청
        private Dictionary<String, String> dicComboBox = new Dictionary<string, string>();
        // 응답코드별 응답
        private Dictionary<String, String> dicResponse = new Dictionary<string, string>();
        // 초기화 정보
        private Dictionary<String, Dictionary<String, String>> dicInit = new Dictionary<string, Dictionary<string, string>>();

        // 전문정의
        private Dictionary<String, Dictionary<String, int>> dicComm = new Dictionary<string, Dictionary<string, int>>();

        private String m_data;

        private const int GROUPBOX_MARGINY = 15;
        private const int TEXTBOX_HEIGHT = 0;
        private const int CONTROL_MARGINY = 10;
        private const int LABEL_MARGINY = 5;

        //=====================================================================================
        //=====================================================================================
        /// <summary>
        /// INI파일에섹션과키로검색하여값을문자열형으로읽어옵니다.
        /// </summary>
        /// <param name="lpAppName">섹션명
        /// <param name="lpKeyName">키값
        /// <param name="lpDefault">기본값
        /// <param name="lpReturnedString">가져온문자열
        /// <param name="nSize">문자열버퍼크기
        /// <param name="lpFileName">파일이름
        /// <returns>가져온문자열의크기</returns>
        [DllImport("kernel32")]
        public static extern int GetPrivateProfileString(string lpAppName, string lpKeyName,
        string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);

        /// <summary>
        /// INI파일에섹션으로검색하여키와값을Pair형태로가져옵니다.
        /// </summary>
        /// <param name="IpAppName">섹션명
        /// <param name="IpPairValues">Pair한키와값을담을배열
        /// <param name="nSize">배열의크기
        /// <param name="IpFileName">파일이름
        /// <returns>읽어온바이트수</returns>
        [DllImport("kernel32.dll")]
        public static extern uint GetPrivateProfileSection(string IpAppName, byte[] IpPairValues,
                                                           uint nSize, string IpFileName);

        delegate void SetTextCallback(String text);
        
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            m_server_ip = GetIniValue2("server", "ip");
            m_server_port = GetIniValue2("server", "port");
            
            String[] arszHeader = GetIniValue1("header");
            String[] arszProduct = GetIniValue1("제품");
            String[] arszComboBox = GetIniValue1("선택박스별요청");
            String[] arszResponse = GetIniValue1("응답코드별응답");

            foreach (String st in arszHeader)
            {
                String[] arszTmp = st.Split(new char[1] {'=' });
                dicHeader.Add(arszTmp[0], Int32.Parse(arszTmp[1]));
            }

            foreach (String st in arszProduct)
            {
                String[] arszTmp = st.Split(new char[1] { '=' });
                dicProduct.Add(arszTmp[0], Int32.Parse(arszTmp[1]));
            }

            foreach (String st in arszComboBox)
            {
                String[] arszTmp = st.Split(new char[1] { '=' });
                dicComboBox.Add(arszTmp[0], arszTmp[1]);

                // 초기화세팅
                String[] arszInit = GetIniValue1(arszTmp[0] + "초기값");
                dicInit.Add(arszTmp[0], new Dictionary<string, string>());
                foreach (String st1 in arszInit)
                {
                    String[] arszTmp1 = st1.Split(new char[1] { '=' });
                    dicInit[arszTmp[0]].Add(arszTmp1[0], arszTmp1[1]);
                }

                // 요청전문정의
                if (!dicComm.ContainsKey(arszTmp[1]))
                {
                    dicComm.Add(arszTmp[1], new Dictionary<string, int>());

                    String[] arszTmp2 = GetIniValue1(arszTmp[1]);
                    foreach (String st1 in arszTmp2)
                    {
                        String[] arszTmp3 = st1.Split(new char[1] { '=' });
                        dicComm[arszTmp[1]].Add(arszTmp3[0], Int32.Parse(arszTmp3[1]));
                    }
                }
            }

            foreach (String st in arszResponse)
            {
                String[] arszTmp = st.Split(new char[1] { '=' });
                dicResponse.Add(arszTmp[0], arszTmp[1]);

                // 응답전문정의
                if (!dicComm.ContainsKey(arszTmp[1]))
                {
                    dicComm.Add(arszTmp[1], new Dictionary<string, int>());

                    String[] arszTmp2 = GetIniValue1(arszTmp[1]);
                    foreach (String st1 in arszTmp2)
                    {
                        String[] arszTmp3 = st1.Split(new char[1] { '=' });
                        dicComm[arszTmp[1]].Add(arszTmp3[0], Int32.Parse(arszTmp3[1]));
                    }
                }
            }

            draw_header();
            draw_combobox();
            comboBox1.SelectedIndex = 0;
        }

        // 헤더 화면 출력
        private void draw_header()
        {
            int prevY=GROUPBOX_MARGINY;
            foreach(KeyValuePair<String, int> kv in dicHeader){
                // 라벨추가
                Label lb = new Label();
                lb.AutoSize = true;
                lb.Location = new System.Drawing.Point(10, prevY+LABEL_MARGINY);
                lb.Name = "LBL_" + kv.Key;
                lb.Text = kv.Key;

                GRP_header.Controls.Add(lb);

                // 텍스트박스
                TextBox tbx = new TextBox();
                tbx.Location = new System.Drawing.Point(120, prevY);
                tbx.MaxLength = kv.Value;
                tbx.Name = "TBX_" + kv.Key;
                tbx.Size = new System.Drawing.Size(100, TEXTBOX_HEIGHT);
                GRP_header.Controls.Add(tbx);

                prevY = prevY + lb.Height + CONTROL_MARGINY;
            }

            // 그룹높이 설정
            GRP_header.Height = prevY + GROUPBOX_MARGINY;

            // 공통초기값 세팅
            String[] arszCommonInit = GetIniValue1("초기값");

            foreach (String st in arszCommonInit)
            {
                String[] arszTmp = st.Split(new char[1] { '=' });
                TextBox tb = (TextBox)GRP_header.Controls.Find("TBX_" + arszTmp[0], false)[0];
                tb.Text = arszTmp[1];
            }
        }

        // 서비스 콤보박스 출력
        private void draw_combobox()
        {
            foreach (KeyValuePair<String, String> kv in dicComboBox)
            {
                comboBox1.Items.Add(kv.Key);
            }
        }

        // INI파일읽기함수(섹션설정)
        public String[] GetIniValue1(string Section)
        {
            byte[] ba = new byte[1024];
            uint Flag = GetPrivateProfileSection(Section, ba, (uint)ba.Length, AppDomain.CurrentDomain.BaseDirectory + Form1.INI_FILE);
            return Encoding.Default.GetString(ba).Split(new char[1] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
        }

        // INI파일읽기함수(섹션,키값설정)
        public string GetIniValue2(string Section, string Key)
        {
            StringBuilder sb = new StringBuilder(500);
            int Flag = GetPrivateProfileString(Section, Key, "", sb, 500, AppDomain.CurrentDomain.BaseDirectory + Form1.INI_FILE);
            return sb.ToString();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox obj = (ComboBox)sender;
            GRP_body.Controls.Clear();
            GRP_product.Controls.Clear();

            String szRequestKey = dicComboBox[obj.Text];
            String szInitKey = obj.Text;

            int prevY = GROUPBOX_MARGINY;
            foreach (KeyValuePair<String, int> kv in dicComm[szRequestKey])
            {
                // 라벨추가
                Label lb = new Label();
                lb.AutoSize = true;
                lb.Location = new System.Drawing.Point(10, prevY + LABEL_MARGINY);
                lb.Name = "LBL_" + kv.Key;
                lb.Text = kv.Key;

                GRP_body.Controls.Add(lb);

                // 텍스트박스
                TextBox tbx = new TextBox();
                tbx.Location = new System.Drawing.Point(120, prevY);
                tbx.MaxLength = kv.Value;
                tbx.Name = "TBX_" + kv.Key;
                tbx.Size = new System.Drawing.Size(100, TEXTBOX_HEIGHT);
                GRP_body.Controls.Add(tbx);

                if (kv.Key.Equals("제품개수"))
                {
                    tbx.TextChanged += new EventHandler(tbx_TextChanged);
                }

                prevY = prevY + lb.Height + CONTROL_MARGINY;
            }

            // 그룹높이 설정
            GRP_body.Height = prevY + GROUPBOX_MARGINY;

            // 초기값 세팅

            foreach (KeyValuePair<String, String> kv in dicInit[szInitKey])
            {
                try
                {
                    TextBox tb = (TextBox)GRP_header.Controls.Find("TBX_" + kv.Key, false)[0];
                    tb.Text = kv.Value;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }

                try
                {
                    TextBox tb = (TextBox)GRP_body.Controls.Find("TBX_" + kv.Key, false)[0];
                    tb.Text = kv.Value;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }

            // 거래일시 세팅
            ((TextBox)GRP_header.Controls.Find("TBX_거래일시", false)[0]).Text = DateTime.Now.ToString("yyyyMMddHHmmss");
        }

        void tbx_TextChanged(object sender, EventArgs e)
        {
            //throw new NotImplementedException();
            TextBox obj = (TextBox)sender;
            GRP_product.Controls.Clear();

            // 제품설정
            int iProductCount = 0;
            try
            {
                iProductCount = Int32.Parse(obj.Text);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                iProductCount = 0;
            }

            int prevY = GROUPBOX_MARGINY;

            for (int i = 0; i < iProductCount; i++)
            {
                foreach (KeyValuePair<String, int> kv in dicProduct)
                {
                    // 라벨추가
                    Label lb = new Label();
                    lb.AutoSize = true;
                    lb.Location = new System.Drawing.Point(10, prevY + LABEL_MARGINY);
                    lb.Name = "LBL_" + kv.Key + i.ToString();
                    lb.Text = kv.Key;

                    GRP_product.Controls.Add(lb);

                    // 텍스트박스
                    TextBox tbx = new TextBox();
                    tbx.Location = new System.Drawing.Point(120, prevY);
                    tbx.MaxLength = kv.Value;
                    tbx.Name = "TBX_" + kv.Key + i.ToString();
                    tbx.Size = new System.Drawing.Size(100, TEXTBOX_HEIGHT);
                    GRP_product.Controls.Add(tbx);

                    prevY = prevY + lb.Height + CONTROL_MARGINY;
                }
            }

            // 그룹높이 설정
            GRP_product.Height = prevY + GROUPBOX_MARGINY;
        }

        private void BTN_전송_Click(object sender, EventArgs e)
        {
            String header = "";
            String body = "";

            String szRequestKey = dicComboBox[comboBox1.Text];

            TBX_result.Text += "전송중...\r\n";

            foreach (KeyValuePair<String, int> kv in dicComm[szRequestKey])
            {
                String textVal = ((TextBox)GRP_body.Controls.Find("TBX_" + kv.Key, false)[0]).Text;
                String val = new String(' ', kv.Value) + textVal;
                body += val.Substring(val.Length - kv.Value);
                TBX_result.Text += kv.Key + ": " + textVal + "\r\n";

                if (kv.Key.Equals("제품개수"))
                {
                    int iProductCount = 0;
                    try
                    {
                        iProductCount = Int32.Parse(textVal);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex.Message);
                        iProductCount = 0;
                    }

                    for (int i = 0; i < iProductCount; i++)
                    {
                        foreach (KeyValuePair<String, int> kv1 in dicProduct)
                        {
                            String textVal1 = ((TextBox)GRP_product.Controls.Find("TBX_" + kv1.Key + i.ToString(), false)[0]).Text;
                            String val1 = new String(' ', kv1.Value) + textVal1;
                            body += val1.Substring(val1.Length - kv1.Value);

                            TBX_result.Text += kv1.Key + ": " + textVal1 + "\r\n";
                        }
                    }
                }
            }

            foreach (KeyValuePair<String, int> kv in dicHeader)
            {
                String textVal = ((TextBox)GRP_header.Controls.Find("TBX_" + kv.Key, false)[0]).Text;
                if (kv.Key.Equals("본문길이"))
                {
                    textVal = body.Length.ToString();
                }
                String val = new String(' ', kv.Value) + textVal;
                header += val.Substring(val.Length - kv.Value);
            }

            m_data = header + body;

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint _ipep = new IPEndPoint(IPAddress.Parse(m_server_ip), Int32.Parse(m_server_port));

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.RemoteEndPoint = _ipep;
            args.Completed += new EventHandler<SocketAsyncEventArgs>(Connect_Completed);
            socket.ConnectAsync(args);
        }

        private void Connect_Completed(object sender, SocketAsyncEventArgs e)
        {
            Socket client = (Socket)sender;


            if (client.Connected)
            {
                byte[] d = Encoding.Default.GetBytes(m_data);
                client.Send(d);

                byte[] data = new byte[1024];
                SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs();
                receiveArgs.SetBuffer(data, 0, 1024);
                receiveArgs.UserToken = client;
                receiveArgs.Completed += new EventHandler<SocketAsyncEventArgs>(Receive_Completed);
                client.ReceiveAsync(receiveArgs);
            }
        }

        private void Receive_Completed(object sender, SocketAsyncEventArgs e)
        {
            Socket _client = (Socket)sender;
            //Telegram _telegram = (Telegram)e.UserToken;
            //_telegram.SetLength(e.Buffer);
            //_telegram.InitData();
            //if (_client.Connected)
            //{
            //    _client.Receive(_telegram.Data, _telegram.DataLength, SocketFlags.None);
            //    System.Diagnostics.Debug.WriteLine(_telegram.GetData());
            //    _client.ReceiveAsync(e);
            //}

            if (_client.Connected && e.BytesTransferred > 0)
            {
                byte[] _receive = e.Buffer;
                String receive = Encoding.Default.GetString(_receive);

                String szRspCode = "", szProductType="";
                int iHeaderLen;
                String header = parseHeader(receive, out szRspCode, out iHeaderLen, out szProductType);
                byte[] _body = new byte[_receive.Length - iHeaderLen];

                Buffer.BlockCopy(_receive, iHeaderLen, _body, 0, _body.Length);

                // 기프트카드 조회는 응답이 틀림
                if (szRspCode.Equals("0110") && szProductType.Equals("4"))
                {
                    szRspCode = "0410";
                }

                this.setResult("\r\n===== 응답해더 =====\r\n" + header);
                this.setResult("\r\n===== 응답본문 =====\r\n" + parseBody(szRspCode, _body));
            }

            _client.Shutdown(SocketShutdown.Both);
            _client.Disconnect(false);
            _client.Close();
        }

        // 헤더파싱
        private String parseHeader(String szRsp, out String szRspCode, out int iHeaderLen, out String szProductType){
            String szRtn = "";
            int iPos = 0;

            szRspCode = "";
            szProductType = "";
     
            foreach(KeyValuePair<String, int> kv in dicHeader){
                String szVal = szRsp.Substring(iPos, kv.Value);

                szRtn += kv.Key + ": " + szVal + "\r\n";

                if (kv.Key.Equals("기능코드"))
                {
                    szRspCode = szVal;
                }
                else if (kv.Key.Equals("모바일상품권유형"))
                {
                    szProductType = szVal;
                }
                iPos += kv.Value;
            }

            iHeaderLen = iPos;

            return szRtn;
        }

        // 본문파싱
        private String parseBody(String szRspCode, byte[] body)
        {
            String szRtn = "";
            int iPos = 0;
            String szResponseKey = dicResponse[szRspCode];

            foreach (KeyValuePair<String, int> kv in dicComm[szResponseKey])
            {
                byte[] arTmp = new byte[kv.Value];
                Buffer.BlockCopy(body, iPos, arTmp, 0, arTmp.Length);
                String szVal = Encoding.Default.GetString(arTmp);
                szRtn += kv.Key + ": " + szVal + "\r\n";

                if (kv.Key.Equals("제품개수"))
                {
                    int iProductCount = 0;
                    try
                    {
                        iProductCount = Int32.Parse(szVal);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex.Message);
                        iProductCount = 0;
                    }

                    iPos += kv.Value;

                    for (int i = 0; i < iProductCount; i++)
                    {
                        foreach (KeyValuePair<String, int> kv1 in dicProduct)
                        {
                            byte[] arTmp1 = new byte[kv1.Value];
                            Buffer.BlockCopy(body, iPos, arTmp1, 0, arTmp1.Length);
                            String szVal1 = Encoding.Default.GetString(arTmp1);
                            szRtn += kv1.Key + i.ToString() + ": " + szVal1 + "\r\n";

                            iPos += kv1.Value;
                        }
                    }
                }
                else
                {
                    iPos += kv.Value;
                }
            }

            return szRtn;
        }

        // 스레드로부터 컨트롤러 접근
        private void setResult(String text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (TBX_result.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(setResult);

                this.Invoke(d, new object[] { text });
            }
            else
            {
                TBX_result.Text += text;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            TBX_result.Text = "";
        }

        private void TBX_result_TextChanged(object sender, EventArgs e)
        {
            TextBox obj = (TextBox)sender;

            obj.SelectionStart = obj.Text.Length + 1;
            obj.ScrollToCaret();
        }
    }
}